package com.xhai.admin.service;

import com.xhai.common.cache.RedisCache;
import com.xhai.common.constant.RedisConstants;
import com.xhai.common.domain.entity.SysUser;
import com.xhai.common.exception.user.UserPasswordErrorCountExceedLimitException;
import com.xhai.common.exception.user.UserPasswordErrorLockException;
import com.xhai.common.exception.user.UserPasswordNotMatchException;
import com.xhai.common.utils.SecurityUtils;
import com.xhai.common.utils.classes.ObjectUtils;
import com.xhai.common.utils.classes.StringUtils;
import com.xhai.security.context.AuthenticationContextHolder;
import com.xhai.service.SysConfigService;
import jakarta.annotation.Resource;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @author xhai
 * @description 登录密码服务
 * @date 2025/5/31
 */
@Component
public class PasswordService {

    @Resource
    private SysConfigService sysConfigService;

    @Resource
    private RedisCache redisCache;

    public String encryptPassword(String password) {
        return password;
    }

    public String decryptPassword(String password) {
        return password;
    }

    public void validate(SysUser user) {
        Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();
        String username = usernamePasswordAuthenticationToken.getName();
        String password = usernamePasswordAuthenticationToken.getCredentials().toString();

        // 判断用户是否在锁定时间
        String lock = redisCache.getCacheObject(RedisConstants.REDIS_KEY_PREFIX_LOCK_TIME + username);
        if (!ObjectUtils.isNull(lock)) {
            throw new UserPasswordErrorLockException();
        }

        // 密码错误最大重试次数
        Integer pwdErrCnt = redisCache.getCacheObject(RedisConstants.REDIS_KEY_PREFIX_PWD_ERROR_COUNT + username);
        if (pwdErrCnt == null) {
            pwdErrCnt = 0;
        }

        // 密码错误超过最大重试次数锁定时间
        Integer lockTime = sysConfigService.selectLoginPasswordFailLockTime();

        // 判断错误次数是否超过最大重试次数
        Integer maxRetryCount = sysConfigService.selectLoginPasswordFailMaxRetryCount();
        if (pwdErrCnt > maxRetryCount) {
            // 锁定用户
            lockUserInRedis(username, lockTime);
            throw new UserPasswordErrorCountExceedLimitException(maxRetryCount, lockTime);
        }

        // 没超过时，检查密码错误
        if (!check(password, user)) {
            pwdErrCnt++;
            redisCache.setCacheObject(RedisConstants.REDIS_KEY_PREFIX_PWD_ERROR_COUNT, pwdErrCnt);
            throw new UserPasswordNotMatchException();
        } else {
            // 密码正确时清除redis错误次数
            clearPwdErrCacheInRedis(username);
        }
    }

    /**
     * 删除redis中密码错误次数缓存
     *
     * @param username 用户名称
     */
    private void clearPwdErrCacheInRedis(String username) {
        if (redisCache.hasKey(RedisConstants.REDIS_KEY_PREFIX_PWD_ERROR_COUNT + username)) {
            redisCache.deleteObject(RedisConstants.REDIS_KEY_PREFIX_PWD_ERROR_COUNT + username);
        }
    }

    /**
     * 校验密码是否正确
     *
     * @param rawPassword 原始密码
     * @param user 用户信息
     * @return 校验结果
     */
    private boolean check(String rawPassword, SysUser user) {
        String password = user.getPassword();
        if (StringUtils.isEmpty(password)) {
            throw new UserPasswordNotMatchException();
        }

        return SecurityUtils.matchesPassword(rawPassword, password);
    }

    /**
     * 在redis锁定用户指定时间
     *
     * @param username 用户名称
     * @param lockTime 锁定时间
     */
    private void lockUserInRedis(String username, Integer lockTime) {
        // 超过最大重试次数时，redis保存用户锁定状态
        redisCache.setCacheObject(RedisConstants.REDIS_KEY_PREFIX_LOCK_TIME + username, "true", lockTime, TimeUnit.MINUTES);
        // 删除redis中用户错误次数
        clearPwdErrCacheInRedis(username);
    }
}
